#define _CRT_SECURE_NO_WARNINGS 1

#include"PageCache.h"

PageCache* PageCache::_pInst = new PageCache();

//获取一个k页的span
Span* PageCache::NewSpan(size_t k)
{
	assert(k > 0);
	if (k > NPAGES - 1)
	{
		//页数大于128，直接向堆申请
		void* ptr = SystemAlloc(k);
		//Span* span = new Span;
		Span* span=_spanPool.New();
		//页号*页大小=该页的起始地址
		span->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
		span->_n = k;

		//_idSpanMap[span->_pageId] = span;//记录pageId和span映射关系,方便释放的时候通过页找到span
		_idSpanMap.set(span->_pageId, span);
		return span;
	}
	//先检查第k个桶里面有没有span
	if (!_spanLists[k].Empty())
	{
		Span* kSpan= _spanLists[k].PopFront();
		
		//建立id和span的映射，方便CentralCache回收小块内存时，查找对应的span
		for (PAGE_ID i = 0; i < kSpan->_n; i++)
		{
			//_idSpanMap[kSpan->_pageId + i] = kSpan;
			_idSpanMap.set(kSpan->_pageId + i, kSpan);
		}
		return kSpan;//kSpan页返回给CentralCache
	}

	//检查后面桶里有没有span
	for (size_t i = k + 1; i < NPAGES; ++i)
	{
		if (!_spanLists[i].Empty())
		{
			Span* nSpan = _spanLists[i].PopFront();
			//Span* kSpan = new Span;
			Span* kSpan = _spanPool.New();

			//在nSpan的头部切下k页
			//k页span返回给CentralCache；nSpan再挂接到对应映射的位置
			kSpan->_pageId = nSpan->_pageId;
			kSpan->_n = k;

			nSpan->_pageId += k;//更新编号
			nSpan->_n -= k;//既是剩余页数也是映射位置

			_spanLists[nSpan->_n].PushFront(nSpan);//挂接

			//存储nSpan的首尾页号跟nSpan映射，方便PageCache回收内存时进行合并查找
			//_idSpanMap[nSpan->_pageId] = nSpan;
			//_idSpanMap[nSpan->_pageId + nSpan->_n - 1] = nSpan;//nSpan最后一个页号

			_idSpanMap.set(nSpan->_pageId, nSpan);
			_idSpanMap.set(nSpan->_pageId + nSpan->_n - 1, nSpan);
	
			//建立id和span的映射，方便CentralCache回收小块内存时，查找对应的span
			for (PAGE_ID i = 0; i < kSpan->_n; i++)
			{
				//_idSpanMap[kSpan->_pageId+i] = kSpan;
				_idSpanMap.set(kSpan->_pageId + i, kSpan);
;			}
			return kSpan;//kSpan页返回给CentralCache
		}
	}
	//走到这说明后面没有大页的span，这时需要去堆要一个128页的span
	//Span* bigSpan = new Span;
	Span* bigSpan = _spanPool.New();

	void* ptr = SystemAlloc(NPAGES - 1);
	bigSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
	bigSpan->_n = NPAGES - 1;

	_spanLists[bigSpan->_n].PushFront(bigSpan);

	return NewSpan(k);
}
//通过页的起始地址找到页，从而映射找到span
Span* PageCache::MapObjToSpan(void* obj)
{
	//算页号
	PAGE_ID id = (PAGE_ID)obj >> PAGE_SHIFT;
	
	//std::unique_lock<std::mutex>lock(_pageMtx);//RAII思想，构造时加锁，出作用域对象销毁调用析构函数解锁
	////查找
	//auto ret = _idSpanMap.find(id);
	//if (ret != _idSpanMap.end())
	//{
	//	return ret->second;
	//}
	//else
	//{
	//	assert(false);
	//	return nullptr;
	//}
	//return ret->second;

	auto ret = (Span*)_idSpanMap.get(id);

	assert(ret != nullptr);
	return ret;
}

void PageCache::ReleaseSpanToPageCache(Span* span)
{
	//大于128页，直接还给堆
	if (span->_n > NPAGES - 1)
	{
		void* ptr = (void*)(span->_pageId << PAGE_SHIFT);
		SystemFree(ptr);
		//delete span;
		_spanPool.Delete(span);
		return;
	}
	//尝试span前后页合并，缓解内存外碎片问题
	while (1)
	{
		PAGE_ID prevId = span->_pageId - 1;

		//auto ret = _idSpanMap.find(prevId);
		////如果没有前面的页号,不合并了
		//if (ret == _idSpanMap.end())
		//{
		//	break;
		//}
		////如果前面的相邻页span在使用，不合并了
		//Span* prevSpan = ret->second;

		auto ret =(Span*) _idSpanMap.get(prevId);
		if (ret == nullptr)
		{
			break;
		}
		Span* prevSpan = ret;
		if (prevSpan->_isUse == true)
		{
			break;
		}

		//如果合并超过128页的span，没办法管理，不合并了
		if (prevSpan->_n + span->_n > NPAGES - 1)
		{
			break;
		}
		//合并
		span->_pageId = prevSpan->_pageId;
		span->_n += prevSpan->_n;

		//合并了要删除挂接在桶上的prevSpan
		_spanLists[prevSpan->_n].Erase(prevSpan);
		//delete prevSpan;
		_spanPool.Delete(prevSpan);
	}
	while (1)
	{
		PAGE_ID nextId = span->_pageId + span->_n;

		//auto ret = _idSpanMap.find(nextId);
		//if (ret == _idSpanMap.end())
		//{
		//	break;
		//}
		//Span* nextSpan = ret->second;

		auto ret = (Span*)_idSpanMap.get(nextId);
		if (ret == nullptr)
		{
			break;
		}
		Span* nextSpan = ret;

		if (nextSpan->_isUse == true)
		{
			break;
		}
		if (span->_n + nextSpan->_n > NPAGES - 1)
		{
			break;
		}
		//合并
		span->_n += nextSpan->_n;

		_spanLists[nextSpan->_n].Erase(nextSpan);
		//delete nextSpan;
		_spanPool.Delete(nextSpan);
	}
	//前后页合并后的span或者无法合并的span挂接到在PageCache对应桶
	_spanLists[span->_n].PushFront(span);
	span->_isUse = false;

	//_idSpanMap[span->_pageId] = span;
	//_idSpanMap[span->_pageId + span->_n - 1] = span;

	_idSpanMap.set(span->_pageId, span);
	_idSpanMap.set(span->_pageId + span->_n - 1, span);

}